我們在前兩篇文章有利用 useEffect 去 fetch
資料,但把這部分和主要元件的程式碼放在一起,會讓整體看起來很冗長,因此這篇文章會說到自定義 Hook 來解決這部分的問題
useFetch.js
const useFetch () =>{
(…)
}
Export default useFetch;
例如我們上一篇文章的這個程式碼:
import { useEffect, useState } from "react";
import BlogList from "./BlogList";
const Home = () => {
const [blogs, setBlogs] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
setTimeout(() => {
fetch('http://localhost:8000/blogs')
.then(res => {
if (!res.ok) {
throw Error(''無法讀取資料來源'');
}
return res.json();
})
.then(data => {
setIsLoading(false);
setBlogs(data);
setError(null);
})
.catch(err => {
setIsLoading(false);
setError(err.message);
})
}, 1000);
}, [])
return (
<div className="home">
{error && <div>{ error }</div>}
{ isLoading && <div>資料載入中...</div> }
{blogs && <BlogList blogs={blogs} />}
</div>
);
}
export default Home;
缺點:程式碼太冗長,而且如果其他元件也需要獲取資料,就需要再寫一遍
useFetch.js
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [blogs, setBlogs] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
setTimeout(() => {
fetch(url)
.then(res => {
if (!res.ok) {
throw Error(''無法讀取資料來源'');
}
return res.json();
})
.then(data => {
setIsLoading(false);
setBlogs(data);
setError(null);
})
.catch(err => {
setIsLoading(false);
setError(err.message);
})
}, 1000);
}, [url])
return { data, isLoading, error };
}
export default useFetch;
因此在原本的元件裡就可以直接導入:
Home.js
import BlogList from "./BlogList";
import useFetch from "./useFetch";
const Home = () => {
const { error, isLoading, data: blogs } = useFetch('http://localhost:8000/blogs')
return (
<div className="home">
{ error && <div>{ error }</div> }
{ isLoading && <div>資料載入中...</div> }
{ blogs && <BlogList blogs={blogs} /> }
</div>
);
}
export default Home;
為什麼抽出邏輯後,原本的
http://localhost:8000/blogs
要改成 url 參數?
如果不傳入 url,Hook 內部的 API 位址就會被固定,只能抓某一個 API;
但如果改成接收 url 參數,就能在呼叫 Hook 時傳入不同的 API,讓同一個 Hook 能夠重複使用
const blogs = useFetch("http://localhost:8000/blogs");
const users = useFetch("http://localhost:8000/users");
使用時可以用同一個 Hook 也能抓取其他 API
沒有 url 參數 | 有 url 參數 | |
---|---|---|
寫法 | useEffect(() => { fetch("http://localhost:8000/blogs") ... }, []) | useFetch("http://localhost:8000/blogs")} |
彈性 | 低,只能抓固定的 API | 高,同一個 Hook 抓取不同的 API |
維護性 | 換 API 需要修改 Hook 內部的程式碼 | 只需要修改呼叫Hook的地方 |
重複使用性 | 幾乎只能用在單一場景 | 可以在多個元件、API 重複使用 |
使用自定義 Hook 需要注意的地方
const { data, isLoading error } = useFetch(url);